<?php

namespace yunj\app\admin\service\auth;

use GuzzleHttp\Promise\Is;
use think\facade\Db;
use yunj\app\admin\enum\auth\AuthPageOpen;
use yunj\app\admin\enum\auth\AuthPageType;
use yunj\app\admin\enum\auth\AuthRequestType;
use yunj\app\admin\enum\auth\AuthType;
use yunj\app\admin\enum\EnableLoginAuth;
use yunj\app\admin\enum\IsDemo;
use yunj\app\admin\enum\IsSystem;
use yunj\app\admin\enum\route\RouteDataType;
use yunj\app\admin\enum\route\RoutePageFormType;
use yunj\app\admin\enum\State;
use yunj\app\admin\enum\TableBuilderEvent;
use yunj\app\admin\service\route\page\RouteListService;
use yunj\app\admin\service\route\RouteRequestService;
use yunj\app\admin\validate\auth\AuthValidate;
use yunj\app\admin\validate\route\RouteValidate;
use yunj\core\builder\YunjForm;
use yunj\core\builder\YunjTable;
use yunj\core\enum\TipsTemplet;
use yunj\core\response\SuccessJson;

class AuthPageService extends Service {

    /**
     * 获取表单类型
     * @return AuthPageType
     */
    public static function getPageType(): AuthPageType {
        static $pageTypeObj;
        if ($pageTypeObj) {
            return $pageTypeObj;
        }
        $pageType = request()->param('pageType');
        if (!$pageType || !AuthPageType::isValue($pageType)) {
            request()->isAjax() ? throw_error_json('错误请求') : throw_redirect(tips_url(TipsTemplet::NOT_FOUND(), '错误请求'));
        }
        $pageTypeObj = AuthPageType::byValue($pageType);
        return $pageTypeObj;
    }

    /**
     * 获取列表构建器
     * @return YunjTable
     */
    public function getListBuilder(): YunjTable {
        $args = [
            'state' => [
                ['key' => 'all', 'title' => '所有'],
                ['key' => AuthPageType::SIDEBAR_MENU, 'title' => '侧边栏菜单'],
                ['key' => AuthPageType::TOP_MENU, 'title' => '顶部菜单'],
                ['key' => State::RECYLE_BIN, 'title' => '回收站'],
            ],
            'filter' => function (YunjTable $builder, $state) {
                return [
                    'keywords' => [
                        'title' => '关键词',
                        'placeholder' => '模糊匹配，名称/KEY',
                        'auth' => 'yunj_auth_list_filter_keywords',
                    ],
                ];
            },
            'toolbar' => function (YunjTable $builder, $state) {
                $pageType = in_array($state, [AuthPageType::SIDEBAR_MENU, AuthPageType::TOP_MENU]) ? $state : AuthPageType::NORMAL;
                $toolbar = [
                    'add' => ['type' => 'openPopup', 'title' => '添加', 'class' => 'layui-icon-add-circle', 'url' => build_url('yunjAdminAuthAdd', ['pageType' => $pageType]), 'auth' => 'yunj_auth_add']
                ];
                if ($state == State::RECYLE_BIN) {
                    $toolbar += [
                        TableBuilderEvent::NORMAL => ['title' => '恢复正常', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_auth_normal'],
                        TableBuilderEvent::DELETED => ['title' => '永久删除', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_auth_deleted'],
                    ];
                } else {
                    $toolbar += [
                        TableBuilderEvent::SORT => ['title' => '树形/排序', 'type' => 'openPopup', 'class' => 'yunj-icon-sort',
                            'url' => build_url('yunjAdminAuthSort', ['pageType' => $pageType]), 'auth' => 'yunj_auth_sort'],
                        TableBuilderEvent::RECYLE_BIN => ['title' => '移入回收站', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_auth_recyle_bin'],
                    ];
                }
                return $toolbar;
            },
            'defaultToolbar' => function (YunjTable $builder, $state) {
                $defaultToolbar = [
                    'filter',
                    'export' => [
                        'auth' => 'yunj_auth_export'
                    ],
                    'syncSystemDatas' => [
                        'title' => '同步系统权限',
                        'type' => 'asyncEvent',
                        'class' => 'yunj-icon-refresh',
                        'auth' => 'yunj_auth_sync_system_auths'
                    ],
                    'clearCache' => [
                        'title' => '清理缓存',
                        'type' => 'asyncEvent',
                        'class' => 'yunj-icon-clear',
                        'auth' => 'yunj_auth_clear_cache'
                    ]
                ];
                return $defaultToolbar;
            },
            "page" => function (YunjTable $builder, $state) {
                // 回收站开启分页
                return $state == State::RECYLE_BIN;
            },
            "pk" => function (YunjTable $builder, $state) {
                return "key";
            },
            'tree' => function (YunjTable $builder, $state) {
                // 回收站不使用树形展示
                return $state != State::RECYLE_BIN;
            },
            'cols' => function (YunjTable $builder, $state) {
                $pageType = in_array($state, [AuthPageType::SIDEBAR_MENU, AuthPageType::TOP_MENU]) ? $state : AuthPageType::NORMAL;
                $cols = [
                    'key' => ['type' => 'checkbox'],
                    'name' => ['title' => '名称/KEY'],
                    'is_system' => ['title' => '系统权限', 'templet' => 'enum', 'align' => 'center', 'options' => IsSystem::getEnumOptions()],
                    'request_data' => ['title' => '请求配置', 'templet' => 'json'],
                ];

                if ($state == AuthPageType::SIDEBAR_MENU || $state == AuthPageType::TOP_MENU) {
                    $cols += [
                        'icon' => ['title' => '图标', 'templet' => 'icon'],
                        'page_open' => ['title' => '页面打开方式', 'templet' => 'enum', 'options' => AuthPageOpen::getEnumOptions()],
                    ];
                } else {
                    $cols += [
                        'type' => ['title' => '类型', 'templet' => 'enum', 'options' => AuthType::getEnumOptions()],
                    ];
                }

                // action
                $cols['action'] = ['title' => '操作', 'templet' => 'action', 'options' => []];
                $actionOptions = [
                    'edit' => ['type' => 'openPopup', 'title' => '详情', 'class' => 'layui-icon-survey', 'url' => build_url('yunjAdminAuthEdit', ['pageType' => $pageType]), 'auth' => 'yunj_auth_edit'],
                ];
                if ($state == State::RECYLE_BIN) {
                    $actionOptions += [
                        TableBuilderEvent::NORMAL => ['title' => '恢复正常', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_auth_normal'],
                        TableBuilderEvent::DELETED => ['title' => '永久删除', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_auth_deleted']
                    ];
                } else {
                    $titleSuffix = in_array($state, [AuthPageType::SIDEBAR_MENU, AuthPageType::TOP_MENU]) ? '菜单' : '权限';
                    array_insert($actionOptions, [
                        'itemAdd' => ['type' => 'openPopup', 'title' => "添加子{$titleSuffix}", 'class' => 'layui-icon-add-circle', 'url' => build_url('yunjAdminAuthAdd', ['pageType' => $pageType, 'isAddChild' => 'yes']), 'auth' => 'yunj_auth_add'],
                    ], 'edit');
                    $actionOptions[TableBuilderEvent::RECYLE_BIN] = ['title' => '移入回收站', 'type' => 'asyncEvent', 'dropdown' => true, 'auth' => 'yunj_auth_recyle_bin'];
                }
                $cols['action'] = ['title' => '操作', 'templet' => 'action', 'options' => $actionOptions];
                $this->setListBuilderColsAuth($cols);
                return $cols;
            },
            'validate' => AuthValidate::class,
            'count' => function (YunjTable $builder, array $filter) {
                $where = self::getListBuilderFilterWhere($filter);
                return self::getAdminAuthModel()->getOwnCount($where);
            },
            'items' => function (YunjTable $builder, int $page, int $pageSize, array $filter, array $sort) {
                $state = $filter['state'];
                $where = self::getListBuilderFilterWhere($filter);
                $order = [
                    (in_array($state, [AuthPageType::SIDEBAR_MENU, AuthPageType::TOP_MENU]) ? 'menu_sort' : 'sort') => 'asc',
                    'is_system' => 'asc'
                ];
                $items = self::getAdminAuthModel()->getOwnRowsToArray(
                    $where, ['*', 'parent as pkey'], $order
                    , $state == State::RECYLE_BIN ? $page : 0
                    , $state == State::RECYLE_BIN ? $pageSize : 0
                );
                self::handleListBuilderItems($items);
                //dump($items);die;
                return $items;
            },
            'event' => function (YunjTable $builder, $event, array $ids) {
                return $this->handleListBuilderEvent($builder, $event, $ids);
            },
        ];
        return YT('AuthList', $args);
    }

    // 处理异步事件
    private function handleListBuilderEvent(YunjTable $builder, $event, array $keys) {
        // 状态、排序处理
        if ($res = $this->handleListBuilderEventByStateAndSort($event, $keys)) {
            if ($res instanceof SuccessJson) {
                // 修改成功清理缓存
                AuthService::reset();
            }
            return $res;
        }

        // 同步系统路由数据
        if ($event == 'syncSystemDatas') {
            AuthService::syncSystemDatas();
            return success_json(['reload' => true], '同步系统权限成功');
        }
        // 清理缓存
        if ($event == 'clearCache') {
            AuthService::clearCache();
            return success_json(['reload' => true], '缓存清理成功');
        }

        return $res ?: error_json("异常事件[{$event}]");
    }

    // 状态、排序处理
    private function handleListBuilderEventByStateAndSort($event, array $keys) {
        $res = null;
        if (in_array($event, [TableBuilderEvent::NORMAL, TableBuilderEvent::RECYLE_BIN, TableBuilderEvent::DELETED])) {
            $res = TableBuilderEvent::handleEvent(self::getAdminAuthModel(), $event, $keys, function ($eventObj, $keys, array $modelFunParams) {
                $eventVal = $eventObj->getValue();
                // 判断是否有系统权限，系统权限不允许删除
                if ($eventVal !== TableBuilderEvent::SORT) {
                    $hasExistSys = self::getAdminAuthModel()->getOwnCount([
                        ['key', 'in', $keys],
                        ['is_system', '=', IsSystem::YES],
                    ]);
                    if ($hasExistSys) {
                        throw_error_json('系统权限不可' . $eventObj->getDesc());
                    }
                }
                $currTime = time();
                if ($eventVal == TableBuilderEvent::DELETED) {
                    [$data, $where] = $modelFunParams;
                    $data['key'] = Db::raw("concat(`key`,'_del_{$currTime}')");
                    $data['request_id'] = null; // request_id 字段有唯一索引限制
                    $where[] = ['is_system', '<>', IsSystem::YES];
                    $modelFunParams = [$data, $where];
                } elseif ($eventVal == TableBuilderEvent::RECYLE_BIN) {
                    [$data, $where] = $modelFunParams;
                    $where[] = ['is_system', '<>', IsSystem::YES];
                    $modelFunParams = [$data, $where];
                }
                return $modelFunParams;
            });
        }
        return $res;
    }

    // 处理数据
    private static function handleListBuilderItems(array &$items): void {
        if (!$items) {
            return;
        }
        foreach ($items as &$item) {
            ['key' => $key, 'name' => $name, 'request_type' => $requestType, 'request_id' => $requestId, 'request_url' => $requestUrl] = $item;
            // 名称/KEY
            $item['name'] = "【{$name}】{$key}";
            // 追加请求数据
            $itemRequestData = [];
            if (AuthRequestType::isValue($requestType)) {
                /** @var AuthRequestType $requestTypeObj */
                $requestTypeObj = AuthRequestType::byValue($requestType);
                if ($requestType == AuthRequestType::REQUEST_ID && $requestId > 0 && ($requestData = RouteRequestService::getRouteRequestDataById($requestId))) {
                    /** @var State $requestStateObj */
                    $requestStateObj = State::byValue($requestData['final_state']);
                    $itemRequestData = [
                        $requestTypeObj->getTitle() => $requestData['full_desc'],
                        "状态" => $requestStateObj->getDesc(),
                        "地址" => $requestData['route_base_url'],
                        "Method" => $requestData['method'],
                    ];
                    if (is_json($requestData['require_params'], $requireParams, true) && $requireParams) {
                        $itemRequestData["必要参数"] = $requireParams;
                    }
                } else if ($requestType == AuthRequestType::REQUEST_URL && $requestUrl) {
                    $itemRequestData = [$requestTypeObj->getTitle() => $item['request_url']];
                }
            }
            $item['request_data'] = $itemRequestData ?: [];
        }
    }

    /**
     * 获取列表构建器表单条件
     * @param array $filter
     * @return array
     */
    private static function getListBuilderFilterWhere(array $filter): array {
        $state = $pageType = $filter['state'];
        $keys = $filter['keys'];
        $keywords = $filter['keywords'] ?? '';

        $where = [
            ['state', '=', $state == State::RECYLE_BIN ? State::RECYLE_BIN : State::NORMAL]
        ];
        // 不引入demo
        if (!yunj_config('admin.use_demo')) {
            $where[] = ['is_demo', '=', IsDemo::NO];
        }
        if (AuthPageType::isValue($pageType) && ($pageTypeObj = AuthPageType::byValue($pageType)) && $pageTypeObj->isMenu()) {
            /** @var AuthPageType $pageTypeObj */
            $where[] = ['type', '=', $pageTypeObj->getDbType()];
        }
        if ($keywords) {
            $where[] = ['key|full_name', 'like', '%' . $keywords . '%'];
        }
        if ($keys) $where[] = ['key', 'in', $keys];
        return $where;
    }


    /**
     * 设置列表构建器cols表头权限
     * @param array $cols
     */
    private function setListBuilderColsAuth(array &$cols): void {
        foreach ($cols as $k => $v) {
            $cols[$k]['auth'] = 'yunj_auth_list_view_normal';
        }
    }

    /**
     * 获取表单构建器
     * @param bool $isEdit 是否编辑页
     * @param bool $isAddChild 是否添加子项编辑页
     * @return YunjForm
     */
    public function getFormBuilder(bool $isEdit = false, bool $isAddChild = false): YunjForm {
        $args = [
            'field' => function (YunjForm $builder) use ($isEdit, $isAddChild) {
                $field = [
                    // 是否编辑
                    'is_edit' => ['type' => 'hidden', 'verify' => 'require|in:0,1', 'default' => $isEdit ? 1 : 0],
                    // 是否添加子权限
                    'is_add_child' => ['type' => 'hidden', 'verify' => 'require|in:0,1', 'default' => $isAddChild ? 1 : 0],
                    // 添加子权限，父权限不能改变
                    'parent' => ['title' => '父权限', 'type' => 'dropdownSearch', 'readonly' => $isAddChild, 'multi' => false,
                        'options' => build_url('yunjAdminAuthDropdownsearch'), 'optionIdKey' => 'key',
                        'desc' => '不选择则为顶级权限'],
                    // 编辑时不能修改
                    'key' => ['title' => 'KEY', 'verify' => 'require|max:50', 'readonly' => $isEdit, 'desc' => '全局唯一标识。创建后不能更改'],
                    'name' => ['title' => '名称', 'verify' => 'require|max:50'],
                    'desc' => ['title' => '描述', 'verify' => 'max:200'],
                    'type' => ['title' => '类型', 'verify' => 'require', 'type' => 'radio', 'options' => AuthType::getTitleOptions(), 'default' => self::getPageType()->getDbType()],
                    'icon' => ['title' => '图标', 'type' => 'icon'],
                    'page_open' => ['title' => '页面打开方式', 'type' => 'radio', 'options' => AuthPageOpen::getTitleOptions(), 'default' => AuthPageOpen::NIL],
                    'request_type' => ['title' => '请求类型', 'type' => 'radio', 'options' => AuthRequestType::getTitleOptions(), 'default' => AuthRequestType::REQUEST_ID],
                    //'request_id' => ['title' => '路由请求项配置', 'type' => 'dropdownSearch', 'multi' => false, 'options' => build_url('yunjAdminAuthRequestDropdownsearch'), 'desc' => '每个路由请求项只能对应一个权限'],
                    'request_id' => ['title' => '路由请求项配置', 'type' => 'tree', 'mode' => 'radio', 'maxHeight' => 350, 'allOptional' => true, 'nodes' => $this->getRouteRequestTreeNodes(), 'desc' => '每个路由请求项只能对应一个权限'],
                    'request_url' => ['title' => '请求地址配置', 'verify' => 'max:200'],
                ];

                $requestData = $builder->getLoadValues();

                if ($isEdit) {
                    // 权限
                    $this->setFormBuilderFieldAuth($field);
                    // 判断是否有提交权限
                    $hasAuth = admin_member_auth_check('yunj_auth_edit_submit');
                    if (!$hasAuth) {
                        foreach ($field as &$v) $v['readonly'] = true;
                    }
                    // 路由请求项配置节点
                    if ($requestData['request_id'] ?? null) {
                        $nodes = $field['request_id']['nodes'];
                        foreach ($nodes as &$node) {
                            if ($node['id'] == $requestData['request_id']) {
                                $node['name'] = str_replace('【已使用】', '', $node['name']);
                                $node['nocheck'] = false;
                                $node['readonly'] = false;
                            }
                        }
                        $field['request_id']['nodes'] = $nodes;
                    }
                }

                // 是否为系统配置
                $isSystem = ($requestData['is_system'] ?? null) == IsSystem::YES;
                if ($isSystem) {
                    array_insert($field, [
                        'tips' => [
                            'type' => 'txt',
                            'desc' => '温馨提示  系统配置权限不允许修改哦！',
                            'color' => '#FF5722',
                        ]
                    ], 'is_edit');
                    foreach ($field as &$v) $v['readonly'] = true;
                }
                // 设置栅格布局
                foreach ($field as &$v) {
                    $v['grid'] = [12, 6, "6 l3 r3"];
                }
                return $field;
            },
            'validate' => AuthValidate::class,
            'button' => function ($builder) use ($isEdit) {
                $authData = $builder->getLoadValues();
                if ($authData && ($authData['is_system'] ?? null) == IsSystem::YES) {
                    return [];
                }
                if ($isEdit) {
                    if (admin_member_auth_check('yunj_auth_edit_submit')) {
                        $button = ['reset', 'submit'];
                    }
                } else {
                    $button = ['clear', 'submit'];
                }
                return $button ?? [];
            },
            'submit' => function (YunjForm $builder, array $data) {
                $loadValues = $builder->getLoadValues();
                return $this->formBuilderSubmit($loadValues, $data);
            }
        ];
        if ($isEdit || $isAddChild) {
            $args['load'] = function () use ($isAddChild) {
                return $this->formBuilderLoad($isAddChild);
            };
        }
        return YF('AuthForm', $args);
    }

    // 获取可选的路由请求项配置节点数据
    private function getRouteRequestTreeNodes(): array {
        $where = [
            ['state', '=', State::NORMAL]
        ];
        $order = ['sort' => 'asc', 'id' => 'asc'];
        $items = RouteListService::getListBuilderQuery()->where($where)->order($order)->select()->toArray();
        $nodes = [];
        $hasRequestIds = self::getAdminAuthModel()->getColumnOptions('request_id', [['request_id', 'is not null']]);
        foreach ($items as &$item) {
            // id
            $id = $item['id'];
            if ($item['type'] == RouteDataType::REQUEST) {
                $id = str_replace(RouteDataType::REQUEST . '_', '', $item['id']);
            }
            // name
            $name = "【{$item['type_txt']}】" . handle_route_rule($item['name']);
            if (in_array($item['type'], [RouteDataType::REQUEST])) {
                $name .= '【' . ($item['method'] ?: 'ANY') . '】';
            }
            $nocheck = true;
            $checked = false;
            $readonly = true;
            if ($item['type'] == RouteDataType::REQUEST) {
                if (in_array($id, $hasRequestIds)) {
                    $name .= '【已使用】';
                } else {
                    $nocheck = false;
                    $readonly = false;
                }
            }
            $nodes[] = [
                'id' => $id,
                'name' => $name,
                'pid' => $item['pid'],
                'nocheck' => $nocheck,
                'checked' => $checked,
                'readonly' => $readonly,
            ];
        }
        return $nodes;
    }

    // 设置表单构建器字段权限
    private function setFormBuilderFieldAuth(array &$field): void {
        foreach ($field as $k => $v) {
            $field[$k]['auth'] = 'yunj_auth_edit_detail_normal';
        }
    }

    /**
     * 表单构建器load
     * @return array|string
     */
    private function formBuilderLoad(bool $isAddChild = false) {
        $key = request()->get('key');
        if (!$key) return '数据异常';
        if ($isAddChild) {
            $data = [
                'parent' => $key,
                'type' => self::getPageType()->getDbType()
            ];
        } else {
            $data = self::getAdminAuthModel()->getOwnRowToArray([
                ['key', '=', $key],
                ['state', '<>', State::DELETED],
            ]);
            if (!$data) {
                return '数据获取异常';
            }
        }
        return $data;
    }

    // 表单数据提交
    private function formBuilderSubmit(array $loadValues, array $data) {
        $isEdit = $data['is_edit'];
        // 触发设置保存前事件
        event('AdminAuthSaveBefore', ['isEdit' => $isEdit, 'loadValues' => $loadValues, 'data' => $data]);
        try {
            $model = self::getAdminAuthModel();
            $isEdit ? $model->change($data['dbData']) : $model->addRow($data['dbData'], false);
            // 清理缓存
            AuthService::reset();
            return success_json(["reload" => true]);
        } catch (\Exception $e) {
            log_exception($e);
            return error_json(($isEdit ? '修改' : '新增') . '失败');
        }
    }

}